package lib

import (
	"errors"
	"fmt"
	"path"
	"strconv"
	"strings"
	"sync"
)

type Deploy struct {
	config    *Config
	temp_path string
	lock      *sync.RWMutex
}

func NewDeploy(c *Config, TempPath string) *Deploy {
	return &Deploy{
		config:    c,
		temp_path: TempPath,
		lock:      new(sync.RWMutex),
	}
}

func (d *Deploy) List() []SVNItem {
	return d.config.Items
}

func (d *Deploy) FindByName(name string) (item SVNItem, index int, err error) {
	d.lock.RLock()
	defer d.lock.RUnlock()

	for i, ele := range d.config.Items {
		if ele.Name == name {
			item = ele
			index = i
			return
		}
	}
	err = errors.New("item not exist")
	return
}

func (d *Deploy) AddItem(item SVNItem) (err error) {
	if len(item.Name) == 0 {
		return errors.New("Name of SVNItem can't been empty")
	}
	_, _, err1 := d.FindByName(item.Name)
	if err1 == nil {
		err = errors.New(fmt.Sprintf("item with name [%s] exist", item.Name))
		return
	}

	d.lock.Lock()
	defer d.lock.Unlock()

	items := d.config.Items
	d.config.Items = append(items, item)
	err = d.config.Save()
	if err != nil {
		ErrLogger.Println(err)
	}
	return
}

func (d *Deploy) EditByName(name string, item SVNItem) (err error) {
	_, index, err1 := d.FindByName(name)
	if err1 != nil {
		err = err1
		return
	}
	d.lock.Lock()
	defer d.lock.Unlock()

	d.config.Items[index] = item
	err = d.config.Save()
	if err != nil {
		ErrLogger.Println(err)
	}
	return
}

func (d *Deploy) GetItemVersion(ssh *SSHClient, item *SVNItem) int {
	result, err := ssh.Run(fmt.Sprintf("cat \"%s/version\"", item.LocalPath))
	if err != nil {
		return 0
	}

	current_version, err := strconv.Atoi(result)
	if err != nil {
		ErrLogger.Println(err)
		return 0
	}

	return current_version
}

func (d *Deploy) makeDeleteFileCMD(file_paths []string, local_path string) (cmd string) {
	paths := make([]string, len(file_paths))
	for i, ele := range file_paths {
		paths[i] = fmt.Sprintf("rm -Rf \"%s/%s\"", strings.TrimRight(local_path, "/"), strings.TrimLeft(ele, "/"))
	}
	cmd = strings.Join(paths, ";")
	return
}

func (d *Deploy) DeployItem(name string) (err error) {
	var deleteFiles []string
	var pkgFile string
	item, _, err1 := d.FindByName(name)
	if err1 != nil {
		ErrLogger.Println(err1)
		err = err1
		return
	}
	ssh := NewSSHClient(&item.SSH)
	InfoLogger.Println("=== begin get remote version ===")
	version := d.GetItemVersion(ssh, &item)
	InfoLogger.Println("=== remove version:", version, " ===")
	InfoLogger.Println("=== begin export and zip package ===")
	svn := NewSVNWrapper(d.config.SVNBin, &item)
	pkgFile, deleteFiles, err = svn.PackageUpdate(strconv.Itoa(version), "HEAD", d.temp_path)
	if err != nil {
		return
	}
	InfoLogger.Println("=== begin upload file ===")
	err = ssh.UploadFile(pkgFile, item.LocalPath)
	if err != nil {
		return
	}
	InfoLogger.Println("=== begin unzip file ===")
	remote_pkg := fmt.Sprintf("%s/%s", item.LocalPath, path.Base(pkgFile))
	_, err = ssh.Run(fmt.Sprintf("unzip -oq \"%s\" -d \"%s\"", remote_pkg, item.LocalPath))
	if err != nil {
		ErrLogger.Println(err)
		return
	}
	InfoLogger.Println("=== begin clean file ===")
	_, err = ssh.Run(d.makeDeleteFileCMD(deleteFiles, item.LocalPath))
	if err != nil {
		ErrLogger.Println(err)
		return
	}
	InfoLogger.Println("=== begin clean zip file ===")
	err = ssh.RemoveFiles([]string{remote_pkg})
	if err != nil {
		ErrLogger.Println(err)
		return
	}
	InfoLogger.Println("=== Job Done! ===")
	return
}
